-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local bios_service = require 'service.bios_service'
local boot_service = require 'service.boot_service'
local boot_option_service = require 'service.boot_options_service'
local bios_factory = require 'factory.bios_factory'
local obj_def = require 'macros.object_def'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local lu = require 'luaunit'
local prop_def = require 'macros.property_def'
local pro_global = require 'macros.property_global'
local struct = require 'pojo.struct_def'
local kmc = require 'mc.kmc'
local KmcClient = require 'service.kmc_client'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local import_export_eng = require 'handler.export_import_engine'
local smbios_service = require 'service.smbios_service'
local boot_config_activate_service = require 'service.boot_config_activate_service'
local open_db = require 'bios.db'
local project_dir = os.getenv('PROJECT_DIR')

local test_common = require 'test_common.utils'
local bus = test_common.dbus_launch('../.dbus', nil, true)

test_zbios_conf = {}

local function save_func()
end

local function construct_db()
    local ok, datas = pcall(require, 'bios.datas')
    if not ok then
        -- 如果没有datas配置，证明当前组件不需要datas，仅打开数据库
        datas = nil
    end
    local db = open_db(':memory:', datas)
    return db
end

local function get_bios_obj()
    return {
        get_system_id = function()
            return 1
        end,
        register_mdb_objects = function ()
        end,
        unregister_mdb_objects = function ()
        end,
        PcieCardBDF = '',
        PcieDiskBDF = '',
        OCPCardBDF = '',
        Slot = 1
    }
end

function test_zbios_conf:test_bean()
    local option_db_obj = {}
    option_db_obj.BootFlags = '/0/0/3/4/5'
    option_db_obj.ValidFlag = '/0/0/0/0/0/0/7/8/9/10/11/12/13/14/15/16'
    option_db_obj.save = save_func

    local boot_db_obj = {}
    boot_db_obj.BootSourceOverrideMode = 'UEFIBoot'
    boot_db_obj.BootOrder = '/HardDiskDrive/PXE/DVDROMDrive/Others'
    boot_db_obj.BootSourceOverrideEnabled = 'Once'
    boot_db_obj.save = save_func

    local bios_db_obj = {}
    bios_db_obj.OldSetupPassword = ''
    bios_db_obj.NewSetupPassword = ''
    bios_db_obj.UserOldSetupPassword = ''
    bios_db_obj.UserNewSetupPassword = ''
    pro_global.G_BIOS_FILE_CHANGE[0] = struct.bios_file_Change.new({})
    bios_db_obj.save = save_func
    local smbios_db_obj = {['Version'] = '1', ['SKUNumber'] = '2', ['Family'] = 3}
    smbios_db_obj.save = save_func
    local smbios_ser = smbios_service.new(smbios_db_obj)
    local bios_ser = bios_service.get_instance(bus, construct_db())
    bios_ser:add_object(get_bios_obj())
    local boot_ser = boot_service.get_instance(boot_db_obj)
    local boot_option_ser = boot_option_service.get_instance(option_db_obj)
    local key_mgmt_client = KmcClient.new(nil, 'bios', true)

    bios_factory.register_bean('bios_service', bios_ser)
    bios_factory.register_bean('boot_service', boot_ser)
    bios_factory.register_bean('boot_options_service', boot_option_ser)
    bios_factory.register_bean('kmc_service', key_mgmt_client)
    import_export_eng.register_config_dealer('Bios', boot_ser)
    import_export_eng.register_config_dealer('SmBios', smbios_ser)

    bios_ser:set_bios_prop('CurrentValueFileName', project_dir .. '/test/test_data/currentvalue.json')
    bios_ser:set_bios_prop('ResultFileName', project_dir .. '/test/test_data/result.json')
    os.execute('touch ../test_data/setting2.json')
    bios_ser:set_bios_prop('SettingFileName', project_dir .. '/test/test_data/setting2.json')
    bios_ser:set_bios_prop('RegistryFileName', project_dir .. '/test/test_data/registry.json')

    kmc.encrypt_data = function(domain_id, cipher_alg_id, hmac_alg_id, plaintext)
        return plaintext
    end
    kmc.decrypt_data = function(domain_id, ciphertext)
        return ciphertext
    end
end

local function construct_ctx()
    local ctx = {}
    ctx.ChanType = 1
    ctx.get_initiator = function()
        return {}
    end

    return ctx
end

function test_zbios_conf:test_conf_mode_switch()
    local boot_ser = bios_factory.get_service('boot_service')
    local ctx = construct_ctx()
    local mode_prop = obj_def.BIOS_BOOT_MODE_SUPPORT

    local _, res = pcall(function()
        return boot_ser:set_boot_mode_switch(ctx)
    end)
    lu.assertEquals(res.name, base_messages.InternalErrorMessage.Name)

    boot_ser:set_boot_prop(mode_prop, true)
    res = boot_ser:set_boot_mode_switch(ctx, true)
    local mode_val = boot_ser:get_boot_prop(mode_prop)
    lu.assertEquals(res, prop_def.RESPONSE_OK)
    lu.assertEquals(mode_val, true)

    res = boot_ser:set_boot_mode_switch(ctx, false)
    mode_val = boot_ser:get_boot_prop(mode_prop)
    lu.assertEquals(res, prop_def.RESPONSE_OK)
    lu.assertEquals(mode_val, false)

    res = boot_ser:set_boot_mode_switch(ctx, false)
    mode_val = boot_ser:get_boot_prop(mode_prop)
    lu.assertEquals(res, prop_def.RESPONSE_OK)
    lu.assertEquals(mode_val, false)
end

function test_zbios_conf:test_conf_clear_cmos()
    local system_id = 1
    local bios_ser = bios_factory.get_service('bios_service')
    local ctx = construct_ctx()
    local _, res = pcall(function()
        return bios_ser:clear_cmos(ctx, system_id)
    end)
    lu.assertEquals(res.name, base_messages.InternalErrorMessage.Name)

    local bios_obj = bios_ser:get_obj(system_id)
    bios_obj.obj['ResetCmos'] = 0
    bios_obj.obj['ResetBiosToDefaultsPending'] = false
    local ok = pcall(function()
        return bios_ser:clear_cmos(ctx, system_id)
    end)
    lu.assertEquals(ok, true)
    lu.assertEquals(bios_obj.obj['ResetCmos'], 1)
    lu.assertEquals(bios_obj.obj['ResetBiosToDefaultsPending'], true)
    bios_obj.obj['ResetCmos'] = nil
    bios_obj.obj['ResetBiosToDefaultsPending'] = nil
end

function test_zbios_conf:test_conf_reset_bios()
    pro_global.G_BIOS_FILE_CHANGE[0] = struct.bios_file_Change.new({})
    local bios_ser = bios_factory.get_service('bios_service')
    local ctx = construct_ctx()
    local _, res = pcall(function()
        return bios_ser:reset_bios(ctx)
    end)
    lu.assertEquals(res.name, base_messages.InternalErrorMessage.Name)
end

function test_zbios_conf:test_conf_reset_pwd()
    local bios_ser = bios_factory.get_service('bios_service')
    local ctx = construct_ctx()
    local res = bios_ser:reset_bios_password(ctx)
    lu.assertEquals(res, prop_def.RESPONSE_ERROR)

    local old_pwd = 'aaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
    local new_pwd = 'aaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
    local _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, 'Admin', old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.InvalidPasswordLengthMessage.Name)

    -- 新密码长度小于8
    old_pwd = 'aaaaaaaaa'
    new_pwd = 'bbbbb'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, 'Admin', old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 新密码长度大于16
    old_pwd = 'aaaaaaaaa'
    new_pwd = 'bbbbbbbbbbbbbbbbbbbbbbbb'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, 'Admin', old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 不包含特殊字符
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '123456789'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    old_pwd = 'aaaaaaaaaa'
    new_pwd = 'qweertaaaa'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    old_pwd = 'aaaaaaaaaa'
    new_pwd = 'QWERTRRRRRQWE'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 包含特殊字符纯数字
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '@123456789'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 包含特殊字符。纯小写
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '@qwertyuiop'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 包含特殊字符。纯大写
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '@QWERTYUIOP'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 包含特殊字符+数字
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '@11111111'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res.name, custom_messages.PasswordComplexityCheckFailMessage.Name)

    -- 都包含
    old_pwd = 'aaaaaaaaaa'
    new_pwd = 'Admin@q12343'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res, prop_def.RESPONSE_OK)

    -- 包含特殊字符，大写，小写
    old_pwd = 'aaaaaaaaaa'
    new_pwd = 'Admin@qaaaaaaa'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res, prop_def.RESPONSE_OK)

    -- 包含特殊字符，小写，数字
    old_pwd = 'aaaaaaaaaa'
    new_pwd = '1234dmin@qaaa'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res, prop_def.RESPONSE_OK)

    -- 包含特殊字符，大写，数字
    old_pwd = 'aaaaaaaaaa'
    new_pwd = 'AAAAA@111111'
    _, res = pcall(function()
        return bios_ser:reset_bios_password(ctx, prop_def.BIOS_PWD_NAME_SUPERVISOR, old_pwd, new_pwd)
    end)
    lu.assertEquals(res, prop_def.RESPONSE_OK)

end

-- ipmi:密码
function test_zbios_conf:test_response_ipmi_password()
    local bios_ser = bios_factory.get_service('bios_service')
    local ctx = construct_ctx()
    local res
    local obj = bios_ser:get_obj(1)
    obj:get_password_string('aaa', 'bbb')
    res = obj:get_password_resp(prop_def.BIOS_CHANGE_SUP_PWD_TYPE_SETUP, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.Success)
    res = obj:get_password_resp(prop_def.BIOS_CHANGE_USER_PWD_TYPE_SETUP, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.Success)
    res = obj:get_password_resp(3, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.UnspecifiedError)

    local req = {}
    req.PasswordType = prop_def.BIOS_CHANGE_SUP_PWD_TYPE_SETUP
    res = obj:get_setting_password(req)
    lu.assertEquals(res.CompletionCode, comp_code.CommandNotAvailable)

    obj:set_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD, '66666')
    local password = obj:get_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD)
    lu.assertEquals(password, '66666')
    obj:clear_encrypt_password(prop_def.BIOS_CHANGE_SUP_PWD_TYPE_SETUP)
    password = obj:get_prop(obj_def.PROPERTY_BIOS_SETUP_PWD_OLD)
    lu.assertEquals(password, '')

    req.Information = '1'
    res = obj:ack_setting_password(req, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.Success)

    req.ParameterSelector = prop_def.BIOS_CHANGE_PWD_SELECTOR_GET
    bios_ser:deal_password(req, ctx)
    req.ParameterSelector = prop_def.BIOS_CHANGE_PWD_SELECTOR_ACK
    bios_ser:deal_password(req, ctx)
    req.ParameterSelector = 5
    res = bios_ser:deal_password(req, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.ParmOutOfRange)

    res = bios_ser:update_bios_password(req, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.CommandNotAvailable)
    req.ManufactureId = 2011
    bios_ser:set_bios_prop(prop_def.SYSTEM_STARTUP_STATE, 0)
    bios_ser:update_bios_password(req, ctx)
end

function test_zbios_conf:test_import_export()
    import_export_eng.export(construct_ctx())
    local file = io.open('../test_data/import.json', 'rb')
    local file_string = file:read('*a')
    import_export_eng.import(construct_ctx(), file_string)
    import_export_eng.export(construct_ctx())
    os.execute('rm -rf ../test_data/setting2.json')
end

function test_zbios_conf:test_deal_cmos()
    local task_running = boot_config_activate_service.task_running
    boot_config_activate_service.task_running = function ()
        return true
    end
    local boot_options_service = bios_factory.get_service('boot_options_service')
    local save_ipmi_boot_config = boot_options_service.save_ipmi_boot_config
    boot_options_service.save_ipmi_boot_config = function ()
        return false
    end

    boot_config_activate_service:on_power_state_changed()

    local params_info = {
        [1] = {
            clear_cmos = 1
        }
    }

    local ctx = construct_ctx()
    local system_id = 1

    local get_service = bios_factory.get_service
    bios_factory.get_service = function ()
        return nil
    end
    local ok = boot_config_activate_service:deal_cmos(params_info, ctx, system_id)
    lu.assertEquals(ok, false)

    bios_factory.get_service = get_service

    ok = boot_config_activate_service:deal_cmos(params_info, ctx, system_id)
    lu.assertEquals(ok, false)

    local bios_ser = bios_factory.get_service('bios_service')
    local bios_obj = bios_ser:get_obj(system_id)
    bios_obj.obj['ResetCmos'] = 0
    bios_obj.obj['ResetBiosToDefaultsPending'] = false

    ok = boot_config_activate_service:deal_cmos(params_info, ctx, system_id)
    lu.assertEquals(ok, true)
    lu.assertEquals(bios_obj.obj['ResetCmos'], 1)
    lu.assertEquals(bios_obj.obj['ResetBiosToDefaultsPending'], true)

    bios_obj.obj['ResetCmos'] = nil
    bios_obj.obj['ResetBiosToDefaultsPending'] = nil
    boot_config_activate_service.task_running = task_running
    boot_options_service.save_ipmi_boot_config = save_ipmi_boot_config
end

function test_zbios_conf:test_update_bios_status()
    local req = {}
    local ctx = construct_ctx()
    req.ManufactureId = 2011
    req.Status = 0
    local system_id = 1

    local bios_ser = bios_factory.get_service('bios_service')
    local bios_obj = bios_ser:get_obj(system_id)

    bios_obj.obj['ResetCmos'] = 1
    local res = bios_ser:update_bios_status(req, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.Success)
    lu.assertEquals(bios_obj.obj['ResetCmos'], 0)

    bios_obj.obj['ResetCmos'] = 1
    bios_obj.obj['ResetBiosToDefaultsPending'] = true
    res = bios_ser:update_bios_status(req, ctx)
    lu.assertEquals(res.CompletionCode, comp_code.Success)
    lu.assertEquals(bios_obj.obj['ResetCmos'], 0)
    lu.assertEquals(bios_obj.obj['ResetBiosToDefaultsPending'], false)
    bios_obj.obj['ResetCmos'] = nil
    bios_obj.obj['ResetBiosToDefaultsPending'] = nil
end